import base64
import json
import secrets
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi import APIRouter, Depends, HTTPException, status
from typing import List, Tuple, Optional, Type
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Request

# from fastapi.responses import HTMLResponse

from tortoise.signals import post_save, post_delete

from ..models import *

# from ..utils.dish_schema import MultidishsId

router = APIRouter(
    prefix="/dish",
    tags=["dish"],
)


security = HTTPBasic()


def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
    correct_username = secrets.compare_digest(credentials.username, "seadog")
    correct_password = secrets.compare_digest(credentials.password, "vimatremote")
    if not (correct_username and correct_password):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect email or password",
            headers={"WWW-Authenticate": "Basic"},
        )
    return credentials.username


# @router.post("/delmulti/")
# async def del_multi(data: MultidishsId):
#     for id in data.ids:
#         try:
#             obj = await dish.get(id=id)
#             data = await dishOut_pydantic.from_tortoise_orm(obj)
#             await obj.delete()
#         # @todo 最好分类下，比如找不到obj的情况
#         except:
#             pass
#     return {"message": "success"}


@router.get("/")
async def get_all_dish():
    return await dishOut_pydantic.from_queryset(Dish.all())


# @router.post("/viminfo")
# async def create_dish_security(dish: dishIn_pydantic, username: str = Depends((get_current_username))):
#     dish.body = base64.b64decode(dish.dict(exclude_unset=True)["body"]).decode()
#     new_obj = await dish.create(**dish.dict(exclude_unset=True))
#     # return await dishIn_pydantic.from_tortoise_orm(new_obj)
#     return "success"


@router.post("/")
async def create_dish(dish: dishIn_pydantic):
    new_obj = await Dish.create(**dish.dict(exclude_unset=True))
    return await dishIn_pydantic.from_tortoise_orm(new_obj)


# @router.get("/{id}")
# async def dish_detail(id: int):
#     obj = await dish.get(id=id)
#     return await dishOut_pydantic.from_tortoise_orm(obj)


# @router.patch("/{id}")
# async def dish_update(id: int, data: dishIn_pydantic):
#     data = data.dict(exclude_unset=True)
#     obj = await dish.get(id=id)
#     await obj.update_from_dict(data)
#     await obj.save()
#     return await dishIn_pydantic.from_tortoise_orm(obj)


# @router.delete("/{id}")
# async def dish_delete(id: int):
#     obj = await dish.get(id=id)
#     data = await dishOut_pydantic.from_tortoise_orm(obj)
#     await obj.delete()
#     return {"delete": data}


# class ConnectionManager:
#     def __init__(self):
#         self.active_connections: List[WebSocket] = []
#         self.clients_id: List[WebSocket] = []

#     async def connect(self, websocket: WebSocket, client_id: str):
#         await websocket.accept()
#         self.active_connections.append(websocket)
#         self.clients_id.append(client_id)

#     def disconnect(self, websocket: WebSocket):
#         client_id = self.active_connections.index(websocket)
#         self.active_connections.remove(websocket)
#         self.clients_id.pop(client_id)

#     async def send_personal_message(self, message: str, websocket: WebSocket):
#         await websocket.send_text(message)

#     # @done 广播时排除自己
#     async def broadcast_exclude_talker(self, message: str, current_talker: WebSocket):
#         for connection in self.active_connections:
#             if not connection is current_talker:
#                 await connection.send_text(message)

#     async def broadcast_all(self, message: str):
#         for connection in self.active_connections:
#             # await connection.send_text(message)
#             await connection.send_json(message)


# manager = ConnectionManager()
# global_websocket = []


# # @todo using enum to limit operation to : add / del / clients-update
# async def process_instance(operation: str, instance: dish):
#     if operation in ("add", "del"):
#         dish_obj_out = await dishOut_pydantic.from_tortoise_orm(instance)
#         data = {"operation": operation}
#         data.update(json.loads(dish_obj_out.json()))  # dish_obj_out.json() 返回结果为str!!!
#         return data


# async def send_online_client_info():
#     data = [{"id": i, "source": str(j.client.host) + ":" + str(j.client.port)} for i, j in zip(manager.clients_id, manager.active_connections)]
#     data = {"clients": data, "operation": "client-info"}
#     # print(data)
#     await manager.broadcast_all(data)

#     # @router.get('/test/')
#     # async def get():
#     #     return HTMLResponse(html)


# @router.websocket("/ws/update/{client_id}")
# async def websocket_endpoint(websocket: WebSocket, client_id: str):
#     await manager.connect(websocket, client_id)
#     await send_online_client_info()
#     try:
#         while True:
#             # @done client should info server,operation type: del,add. server will react accordingly and return proper data
#             # @dish 由于通过signal出发 websocket给client发送数据，这里的数据接受没有意义了
#             data = await websocket.receive_text()
#     except WebSocketDisconnect:
#         manager.disconnect(websocket)
#         await send_online_client_info()
#         # data = [{"id": i, "source": str(j.client.host) + ":" + str(j.client.port)} for i, j in zip(manager.clients_id, manager.active_connections)]
#         # data = {"clients": data, "operation": "client-info"}
#         # # print(data)
#         # await manager.broadcast_all(data)


# @post_save(dish)
# async def create_business(
#     sender: "Type[dish]",
#     instance: dish,
#     created: bool,
#     using_db: "Optional[BaseDBAsyncClient]",
#     update_fields: List[str],
# ) -> None:
#     if created:
#         data = await process_instance("add", instance)
#         if data:
#             await manager.broadcast_all(data)


# @post_delete(dish)
# async def signal_post_delete(sender: "Type[dish]", instance: dish, using_db: "Optional[BaseDBAsyncClient]") -> None:
#     data = await process_instance("del", instance)
#     if data:
#         await manager.broadcast_all(data)
